package core.smartCardReader;

import java.nio.ByteBuffer;
import java.util.UUID;

import javax.smartcardio.Card;
import javax.smartcardio.CardException;
import javax.smartcardio.CommandAPDU;
import javax.smartcardio.ResponseAPDU;

import core.utils.Converter;

@SuppressWarnings("restriction")
public class TokenCard {
	private Card card;

	public TokenCard(Card card) {
		this.card = card;

		try {
			selectTokenReadWriteApplet();
		} catch (CardException e) {
			e.printStackTrace();
		}
	}

	private void selectTokenReadWriteApplet() throws CardException {
		CommandAPDU command = new CommandAPDU(new byte[] { 0x00, (byte) 0xA4, 0x04, 0x00, 0x08, (byte) 0xD0, (byte) 0xD1, (byte) 0xD2, (byte) 0xD3, (byte) 0xD4, (byte) 0xD5, 0x01, 0x01, 0x7F });
		ResponseAPDU commandResponse = card.getBasicChannel().transmit(command);
		validateAPDUResponse(commandResponse);
	}

	public UUID readAdminToken() throws CardException {
		CommandAPDU command = new CommandAPDU(0x00, 0x01, 0x00, 0x00, 0x00);
		ResponseAPDU commandResponse = card.getBasicChannel().transmit(command);
		validateAPDUResponse(commandResponse);

		return Converter.byteArrayToUuid(commandResponse.getData());
	}

	public void writeAdminToken(UUID uuid) throws CardException {
		byte[] apduHeader = new byte[] { 0x00, 0x02, 0x00, 0x00, 0x10 };
		byte[] dataByteArray = Converter.uuidToByteArray(uuid);

		ByteBuffer byteBuffer = ByteBuffer.wrap(new byte[apduHeader.length + dataByteArray.length]);
		byteBuffer.put(apduHeader);
		byteBuffer.put(dataByteArray);

		CommandAPDU command = new CommandAPDU(byteBuffer.array());
		ResponseAPDU commandResponse = card.getBasicChannel().transmit(command);
		validateAPDUResponse(commandResponse);
	}

	public UUID readVotingToken() throws CardException {
		CommandAPDU command = new CommandAPDU(0x00, 0x03, 0x00, 0x00, 0x00);
		ResponseAPDU commandResponse = card.getBasicChannel().transmit(command);
		validateAPDUResponse(commandResponse);

		return Converter.byteArrayToUuid(commandResponse.getData());
	}

	public void writeVotingToken(UUID uuid) throws CardException {
		byte[] apduHeader = new byte[] { 0x00, 0x04, 0x00, 0x00, 0x10 };
		byte[] dataByteArray = Converter.uuidToByteArray(uuid);

		ByteBuffer byteBuffer = ByteBuffer.wrap(new byte[apduHeader.length + dataByteArray.length]);
		byteBuffer.put(apduHeader);
		byteBuffer.put(dataByteArray);

		CommandAPDU command = new CommandAPDU(byteBuffer.array());
		ResponseAPDU commandResponse = card.getBasicChannel().transmit(command);
		validateAPDUResponse(commandResponse);
	}

	public void clearTokens() throws CardException {
		byte[] apduHeader = new byte[] { 0x00, 0x05, 0x00, 0x00, 0x00 };

		CommandAPDU command = new CommandAPDU(apduHeader);
		ResponseAPDU commandResponse = card.getBasicChannel().transmit(command);
		validateAPDUResponse(commandResponse);
	}

	public boolean isAdminToken() throws CardException {
		UUID adminToken = readAdminToken();

		if (adminToken.getMostSignificantBits() == 0 && adminToken.getLeastSignificantBits() == 0) {
			return false;
		} else {
			return true;
		}
	}

	public boolean isVotingToken() throws CardException {
		UUID votingToken = readVotingToken();

		if (votingToken.getMostSignificantBits() == 0 && votingToken.getLeastSignificantBits() == 0) {
			return false;
		} else {
			return true;
		}
	}

	private void validateAPDUResponse(ResponseAPDU commandResponse) throws CardException {
		if (commandResponse.getSW() != Integer.parseInt("9000", 16)) {
			throw new CardException("Wrong Card response: " + Integer.toHexString(commandResponse.getSW()));
		}
	}
}
